#include "raster.h"
#include "raster-gdi.h"

#include <stdlib.h>

typedef struct
{
    HDC hDC;
    HBITMAP hDIB;
} GDITargetData;

/* override value to fix odd compiler warning */
#if defined(LCS_sRGB)
    #undef LCS_sRGB
#endif
#define LCS_sRGB 0x73524742

Raster * __cdecl CreateGDITarget(HDC hDC, unsigned int fmt, unsigned int w, unsigned int h)
{
    BITMAPV5HEADER bmiHeader;

    Raster *ret = (Raster *)malloc(sizeof(Raster));
    GDITargetData *data;
    if(!ret)
        return ret;

    data = (GDITargetData *)malloc(sizeof(GDITargetData));
    if(!data)
    {
        free(ret);
        return NULL;
    }

    /* set pitch to width
     */
    ret->pitch = ret->width = w;
    ret->fmt = fmt;
    ret->height = h;
    ret->clipRect.x = ret->clipRect.y = 0;
    ret->clipRect.w = ret->width;
    ret->clipRect.h = ret->height;

    ZeroMemory(&bmiHeader, sizeof(bmiHeader));
    bmiHeader.bV5Size = sizeof(bmiHeader);
    bmiHeader.bV5Width = ret->pitch;
    bmiHeader.bV5Height = 0 - ret->height; /* normally bitmaps are upside-down
                                                    negative height corrects this */
    bmiHeader.bV5Planes = 1; /* must always be one */
    bmiHeader.bV5BitCount = 32;
    bmiHeader.bV5Compression = BI_BITFIELDS; /* BI_RGB would work for opaque */
    bmiHeader.bV5SizeImage = ret->pitch * ret->height * 4;
    bmiHeader.bV5RedMask =          0x00FF0000;
    bmiHeader.bV5GreenMask =        0x0000FF00;
    bmiHeader.bV5BlueMask =         0x000000FF;
    if(ret->fmt == FORMAT_TARGET_ALPHA)
        bmiHeader.bV5AlphaMask =    0xFF000000;
    bmiHeader.bV5CSType = LCS_sRGB; /* sRGB is the default color space on Windows
                                        and is well supported on most other
                                        systems */
    /* other data not necessary */

    /* must hold onto DC that created DIB Section */
    data->hDC = CreateCompatibleDC(hDC);
    /* CreateDIBSection allocates its own pixels */
    data->hDIB = CreateDIBSection(data->hDC, (const BITMAPINFO *)&bmiHeader,
                                DIB_RGB_COLORS, (void **)&ret->pixels, NULL, 0);
    /* DIB must be selected on its DC to blit. Since our DC only has one DIB,
        we should only need to do this once */
    SelectObject(data->hDC, data->hDIB);

    ret->rasterTargetData = data;
    return ret;
}

void __cdecl DestroyGDITarget(Raster *raster)
{
    GDITargetData *data = (GDITargetData *)raster->rasterTargetData;
    DeleteObject(data->hDIB);
    DeleteDC(data->hDC);
    free(data);
    free(raster);
}

int __cdecl GdiBlitRaster(HDC dc, const RasterRect *dstRect, Raster *raster, const RasterRect *srcRect)
{
    GDITargetData *data = (GDITargetData *)raster->rasterTargetData;
    /* todo: rectangle sanity checks, clipping, maybe even support some blit modes here?*/
    #if defined _MSC_VER || defined __MINGW64
    if(raster->fmt == FORMAT_TARGET_OPAQUE)
    {
        if(dstRect->w == srcRect->w && dstRect->h == srcRect->h)
            return BitBlt(dc, dstRect->x, dstRect->y, dstRect->w, dstRect->h,
                          data->hDC, srcRect->x, srcRect->y, SRCCOPY);
        else
            return StretchBlt(dc, dstRect->x, dstRect->y, dstRect->w, dstRect->h,
                              data->hDC, srcRect->x, srcRect->y, srcRect->w, srcRect->h, SRCCOPY);
    }
    else if(raster->fmt == FORMAT_TARGET_ALPHA)
    {
        /* alpha blit is just slightly more complicated */
        BLENDFUNCTION bf;
        bf.BlendOp = AC_SRC_OVER;
        bf.BlendFlags = 0;
        bf.SourceConstantAlpha = 255; /* use only per-pixel alpha */
        bf.AlphaFormat = AC_SRC_ALPHA;
        return GdiAlphaBlend(dc, dstRect->x, dstRect->y, dstRect->w, dstRect->h,
                             data->hDC, srcRect->x, srcRect->y, srcRect->w, srcRect->h, bf);
    }
    #else
    if(dstRect->w == srcRect->w && dstRect->h == srcRect->h)
        return BitBlt(dc, dstRect->x, dstRect->y, dstRect->w, dstRect->h,
                      data->hDC, srcRect->x, srcRect->y, SRCCOPY);
    else
        return StretchBlt(dc, dstRect->x, dstRect->y, dstRect->w, dstRect->h,
                          data->hDC, srcRect->x, srcRect->y, srcRect->w, srcRect->h, SRCCOPY);
    #endif
    return ERROR_INVALID_INPUT; /* obviously invalid input */
}
